﻿using System;
using DotNetCommon.Extensions;
using System.Linq;
using DBUtil.Builders;
using System.Linq.Expressions;
using System.Collections.Generic;
using DotNetCommon;

namespace DBUtil.Provider.MySql
{
    /// <summary>
    /// MySql操作对象
    /// </summary>
    public partial class MySqlAccess : DBAccess
    {
        #region InjectOtherHooks
        private void InjectOtherHooks()
        {
            //前缀匹配
            InjectHook("prefix:[member]:string:Length", memberHook1);
            InjectHook("prefix:[member]:System.Text.Json.", memberHook2);
            InjectHook("prefix:[member]:", memberHookAll);
            //other
            //getArrayLength
            InjectHook("other:getArrayLength", getArrayLength);
            //getArrayIndex
            InjectHook("other:getArrayIndex", getArrayIndex);
        }
        private object memberHook1(object arg)
        {
            //[member]:string:Length
            var ctx = arg as MemberHookContext;
            return ctx.db.StringSqlSegment.GetLength(ctx.WrapBracket(ctx.ParameterString));
        }
        private object memberHook2(object arg)
        {
            //[member]:System.Text.Json.
            var ctx = arg as MemberHookContext;
            var typeFullName = ctx.TypeFullName;
            if (typeFullName == "System.Text.Json.Nodes.JsonArray"
                || typeFullName == "System.Text.Json.Nodes.JsonObject")
            {
                //为什么直接返回 json_length? 因为 JsonArray/JsonObject 下面公开的只有一个 Count 属性
                return $"json_length({ctx.ParameterString})";
            }
            throw new Exception($"不支持解析的表达式类型: {ctx.Expression}!");
        }
        private object memberHookAll(object arg)
        {
            //[member]:
            var ctx = arg as MemberHookContext;
            var member = ctx.Expression as MemberExpression;
            var typeFullName = ctx.TypeFullName;
            var tmp = ctx.ParameterString;
            if (ctx.IsJsonParameter(member.Expression)
                || tmp?.StartsWith("json_value(") == true
                || tmp?.StartsWith("json_extract(") == true
                || tmp?.StartsWith("json_unquote") == true)
            {
                //json
                var jsonPathWrap = ctx.db.GetJsonPathWrapByPropertyName(member.Member.Name);
                return memberAccess(tmp, jsonPathWrap, true, member.Type);
            }
            return ctx.GetDefault();
        }
        private object getArrayLength(object arg)
        {
            var arr = arg as object[];
            var str = arr[0].ToString();
            var type = arr[1] as Type;
            //string.length byte[].Length
            if (type == typeof(string) || type == (typeof(byte[]))) return $"length({str})";
            return $"json_length({str})";
        }
        private object getArrayIndex(object arg)
        {
            var arr = arg as object[];
            var op1 = arr[0] as string;
            var op2 = arr[1] as string;
            var isConstant = (bool)arr[2];
            var type = arr[3] as Type;
            string member;
            if (isConstant)
            {
                member = $"'$[{op2}]'";
            }
            else
            {
                member = $"concat('$[',{op2},']')";
            }
            return memberAccess(op1, member, isConstant, type);
        }
        #endregion

        private void InitHooks()
        {
            InjectOtherHooks();
            //hook1
            InjectHook("bool System.Collections.Generic.List<T>.Contains(T item)", hook1);
            InjectHook("bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value)", hook1);
            //hook2
            InjectHook("bool System.Text.Json.Nodes.JsonArray.Contains(System.Text.Json.Nodes.JsonNode item)", hook2);
            //hook3
            InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
            InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)", hook3);
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)", hook3);
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value)", hook3);
            InjectHook("bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource> comparer)", hook3);
            //hook4
            InjectHook("TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)", hook4);
            //hook5
            InjectHook("T System.Collections.Generic.List<T>.get_Item(int index)", hook5);
            InjectHook("T System.Collections.Generic.IList<T>.get_Item(int index)", hook5);
            //hook6
            InjectHook("System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)", hook6);
            //hook7
            InjectHook("System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(int index)", hook7);
            //hook8
            InjectHook("string System.Object.ToString()", hook8);
            //hook9
            InjectHook("T System.Text.Json.Nodes.JsonNode.GetValue<T>()", hook9);
            //hook10
            InjectHook("bool System.Text.Json.Nodes.JsonObject.ContainsKey(string propertyName)", hook10);
            //hook11
            InjectHook("bool System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey key)", hook11);
            //hook12
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook12);
            InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)", hook12);
            InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook12);
            InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)", hook12);
            //hook13
            InjectHook("T DotNetCommon.Extensions.StringExtensions.ToObject<T>(string input)", hook13);
            InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.ObjectExtensions.ToJsonObject(System.Object obj)", hook13);
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.ObjectExtensions.ToJsonArray(System.Object obj)", hook13);
            //hook14
            InjectHook("int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source)", hook14);
            //hook15
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.List<T> list, T item)", hook15);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.IList<T> list, T item)", hook15);
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Text.Json.Nodes.JsonNode item)", hook15);
            //hook16
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)", hook16);
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddRangeFluent<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> items)", hook16);
            //hook17
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveAtFluent<T>(System.Collections.Generic.List<T> list, int index)", hook17);
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.RemoveAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index)", hook17);
            //hook18
            InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.RemoveFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key)", hook18);
            InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.RemoveFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key)", hook18);
            //hook19
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.ClearFluent<T>(System.Collections.Generic.List<T> list)", hook19);
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.ClearFluent(System.Text.Json.Nodes.JsonArray jsonArray)", hook19);
            //hook20
            InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.ClearFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic)", hook20);
            InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.ClearFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic)", hook20);
            InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.ClearFluent(System.Text.Json.Nodes.JsonObject jsonObject)", hook20);
            //hook21
            InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Text.Json.Nodes.JsonNode value)", hook21);
            InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, TValue value)", hook21);
            InjectHook("System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, TValue value)", hook21);
            //hook22
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode b)", hook22);
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, T b)", hook22);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, T b)", hook22);
            InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, T b)", hook22);
            //hook23
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.InsertAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode item)", hook23);
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.InsertAtFluent<T>(System.Collections.Generic.List<T> list, int index, T b)", hook23);
            //hook24
            InjectHook("bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)", hook24);
            InjectHook("bool System.Linq.Enumerable.All<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)", hook24);
            InjectHook("bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source)", hook24);
            //hook25
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.List<T> list, T item)", hook25);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, T item)", hook25);
            //hook26
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)", hook26);
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)", hook26);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)", hook26);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items)", hook26);
            //hook27
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, T b)", hook27);
            //hook28
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, System.Predicate<T> match)", hook28);
            //hook29
            InjectHook("int System.Collections.Generic.List<T>.IndexOf(T item)", hook29);
            //hook30
            InjectHook("int System.Collections.Generic.List<T>.FindIndex(System.Predicate<T> match)", hook30);
            //hook31
            InjectHook("TResult DotNetCommon.Extensions.ObjectExtensions.ModifyByDto<TResult>(TResult baseObj, System.Object dto)", hook31);
            //hook32
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, System.Func<T, T> func)", hook32);
            InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, System.Func<T, T> func)", hook32);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, System.Func<T, T> func)", hook32);
            //hook33
            InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
            InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
            InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
            //hook34
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook34);
            //hook35
            InjectHook("int DotNetCommon.Extensions.JsonExtensions.FindIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)", hook35);
            InjectHook("int DotNetCommon.Extensions.ArrayExtensions.FindIndex<T>(T[] arr, System.Predicate<T> match)", hook35);
            InjectHook("int DotNetCommon.Extensions.ListExtensions.FindIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)", hook35);
            //hook36
            InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook36);
            //hook37
            InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Func<System.Text.Json.Nodes.JsonNode, bool> filter, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook37);
            //hook38
            InjectHook("int System.Collections.Generic.List<T>.FindLastIndex(System.Predicate<T> match)", hook38);
            //hook39
            InjectHook("int DotNetCommon.Extensions.JsonExtensions.FindLastIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)", hook39);
            InjectHook("int DotNetCommon.Extensions.ArrayExtensions.FindLastIndex<T>(T[] arr, System.Predicate<T> match)", hook39);
            InjectHook("int DotNetCommon.Extensions.ListExtensions.FindLastIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)", hook39);
            //hook40
            InjectHook("TSource System.Linq.Enumerable.ElementAt<TSource>(System.Collections.Generic.IEnumerable<TSource> source, int index)", hook40);
            //hook41
            InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)", hook41);
            InjectHook("System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)", hook41);
            ////hook42
            //InjectHook("T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T, TValue>(T obj, System.Linq.Expressions.Expression<System.Func<T, TValue>> propSelect, System.Func<TValue, TValue> setValue)", hook42_1);
            //InjectHook("T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T>(T obj, string propName, System.Object val)", hook42_2);
            //InjectHook("T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T, TValue>(T obj, System.Linq.Expressions.Expression<System.Func<T, TValue>> propSelect, TValue val)", hook42_3);
        }
        private string json_contains(string doc1, string doc2)
        {
            return $"json_contains({doc1},{doc2})";
        }
        private object hook1(object arg)
        {
            //"bool System.Collections.Generic.List<T>.Contains(T item)"
            //"bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value)"
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var isOne = method.Name.Contains("List<T>");
            var type = method.GenericTypes.First().type;
            if (type.IsNullable()) type = Nullable.GetUnderlyingType(type);
            (bool isEnumString, Type enumType) res = (false, null);
            if (type.IsEnum) res = ctx.IsEnumString(isOne ? call.Arguments[0] : call.Arguments[1]);

            if (res.isEnumString)
            {
                //非json, json中不允许这么定制
                var str1 = ctx.Visit(isOne ? call.Object : call.Arguments[0]);
                var str2 = ctx.Visit(isOne ? call.Arguments[0] : call.Arguments[1]);
                return $"{str2} in {str1}";
            }
            string op1 = string.Empty, op2 = string.Empty;
            op1 = ctx.Visit(isOne ? call.Object : call.Arguments[0]);
            if (op1.StartsWith('('))
            {
                //非json 如: (1,2,3)
                var arg2 = ctx.Visit(isOne ? call.Arguments[0] : call.Arguments[1]);
                var sql = $"{ctx.WrapBracket(arg2)} in {op1}";
                return sql;
            }
            //处理json
            var arr = ctx.GetJsonSqlSeg(isOne ? [call.Object, call.Arguments[0]] : [call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
            return json_contains(arr[0], arr[1]);
        }
        private object hook2(object arg)
        {
            //"bool System.Text.Json.Nodes.JsonArray.Contains(System.Text.Json.Nodes.JsonNode item)"
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var arr = ctx.GetJsonSqlSeg([call.Object, call.Arguments[0]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
            return json_contains(arr[0], arr[1]);
        }
        private object hook3(object arg)
        {
            //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
            //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value)
            //bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource> comparer)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
            return json_contains(arr[0], arr[1]);
        }
        private string memberAccess(string doc1, string jsonPathWrap, bool isConstantMember, Type returnType)
        {
            var returning = GetReturn(returnType);
            //json_value 要求path必须是常量,如: select json_value('[1,2]',concat('$[','1',']')) 会报错
            //可以使用如下代替: select cast(json_unquote(json_extract('[1,2]',concat('$[','1',']'))) as signed)
            if (isConstantMember)
            {
                //常量
                if (returning.IsNullOrEmptyOrWhiteSpace()) return $"json_value({doc1},{jsonPathWrap})";
                if (returning.Contains("datetime(6)"))
                {
                    //使用convert代替,否则查询返回空,反序列化报错
                    //convert(json_value(properties,'$."time"'),datetime(6))
                    return $"convert(json_value({doc1},{jsonPathWrap}),datetime(6))";
                }
                else
                {
                    //json_value(properties,'$.age' returning signed)
                    return $"json_value({doc1},{jsonPathWrap} {returning})";
                }
            }
            else
            {
                //变量
                if (returning.IsNullOrEmptyOrWhiteSpace()) return $"json_unquote(json_extract({doc1},{jsonPathWrap}))";
                if (returning.Contains("datetime(6)"))
                {
                    //使用convert代替,否则查询返回空,反序列化报错
                    //convert(json_unquote(json_extract(properties,'$."time"')),datetime(6))
                    return $"convert(json_unquote(json_extract({doc1},{jsonPathWrap})),datetime(6))";
                }
                else
                {
                    //cast(json_unquote(json_extract(properties,'$.age')) as signed)
                    return $"cast(json_unquote(json_extract({doc1},{jsonPathWrap}) as {returning.Substring("returning ".Length)})";
                }
            }

        }
        private object hook4(object arg)
        {
            //TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
                return memberAccess(doc, member, true, call.Type);
            }
            member = ctx.Visit(call.Arguments[0]);
            member = $"concat('$.',{member})";
            return memberAccess(doc, member, false, call.Type);
        }
        private object hook5(object arg)
        {
            //T System.Collections.Generic.List<T>.get_Item(int index)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = $"'$[{ret.Data.To<int>()}]'";
                return memberAccess(doc, member, true, call.Type);
            }
            member = ctx.Visit(call.Arguments[0]);
            member = $"concat('$[',{member},']')";
            return memberAccess(doc, member, false, call.Type);
        }
        private object hook6(object arg)
        {
            //System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)
            return hook4(arg);
        }
        private object hook7(object arg)
        {
            //System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(int index)
            return hook5(arg);
        }
        private object hook8(object arg)
        {
            //string System.Object.ToString()
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.Visit(call.Object);
            return ctx.db.ConvertSqlSegment.ConvertAllToString(op1);
        }
        private object hook9(object arg)
        {
            //T System.Text.Json.Nodes.JsonNode.GetValue<T>()
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.Visit(call.Object);
            return setJsonReturn(op1, call.Type);
        }
        private string json_contains_path(string doc1, string jsonPathWrap)
        {
            return $"json_contains_path({doc1},'one',{jsonPathWrap})";
        }
        private object hook10(object arg)
        {
            //bool System.Text.Json.Nodes.JsonObject.ContainsKey(string propertyName)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var db = ctx.db;

            var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
                return json_contains_path(doc, member);
            }
            else
            {
                member = ctx.Visit(call.Arguments[0]);
                return json_contains_path(doc, $"concat('$.',{member})");
            }
        }
        private object hook11(object arg)
        {
            //bool System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey key)
            return hook10(arg);
        }
        private string json_overlaps(string doc1, string doc2)
        {
            return $"json_overlaps({doc1},{doc2})";
        }
        private object hook12(object arg)
        {
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
            //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)
            //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
            //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
            return json_overlaps(arr[0], arr[1]);
        }
        private object hook13(object arg)
        {
            //T DotNetCommon.Extensions.StringExtensions.ToObject<T>(string input)
            //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.ObjectExtensions.ToJsonObject(System.Object obj)
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.ObjectExtensions.ToJsonArray(System.Object obj)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.Visit(call.Arguments[0]);
            return getJsonFormatFromSqlSeg(op1);
        }
        private object hook14(object arg)
        {
            //int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.Visit(call.Arguments[0]);
            return $"json_length({op1})";
        }
        private object hook15(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.List<T> list, T item)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.IList<T> list, T item)
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Text.Json.Nodes.JsonNode item)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
            return getJsonArrayAppend(arr[0], arr[1]);
        }
        private object hook16(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddRangeFluent<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> items)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
            return getJsonArrayMergePreserve(arr[0], arr[1]);
        }
        private object hook17(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveAtFluent<T>(System.Collections.Generic.List<T> list, int index)
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.RemoveAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var _val = ret.Success ? ret.Data : ctx.Visit(call.Arguments[1]);
            return getJsonArrayRemove(doc, _val, ret.Success);
        }
        private object hook18(object arg)
        {
            //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.RemoveFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key)
            //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.RemoveFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                member = $"concat('$.',{member})";
            }
            return getJsonObjectRemove(doc, member);
        }
        private object hook19(object arg)
        {
            return $"json_array()";
        }
        private object hook20(object arg)
        {
            return $"json_object()";
        }
        private string json_set(string doc, string jsonPathWrap, string val)
        {
            return $"json_set({doc},{jsonPathWrap},{val})";
        }
        private string json_set_multi(string doc, List<(string jsonPathWrap, string val)> multi)
        {
            return $"json_set({doc},{multi.Select(i => $"{i.jsonPathWrap},{i.val}").ToStringSeparated(",")})";
        }
        private string json_array_insert(string doc, string jsonPathWrap, string val)
        {
            return $"json_array_insert({doc},{jsonPathWrap},{val})";
        }
        private object hook21(object arg)
        {
            //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Text.Json.Nodes.JsonNode value)
            //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, TValue value)
            //System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, TValue value)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                member = $"concat('$.',{member})";
            }
            var doc = arr[0];
            return json_set(doc, member, arr[1]);
        }
        private object hook22(object arg)
        {
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode b)
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, T b)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, T b)
            //T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, T b)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            var path = string.Empty;
            if (ret.Success)
            {
                member = ret.Data.ToString();
                path = $"'$[{member}]'";
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                path = $"concat('$[',{member},']')";
            }
            var doc = arr[0];
            return json_set(doc, path, arr[1]);
        }
        private object hook23(object arg)
        {
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.InsertAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode item)
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.InsertAtFluent<T>(System.Collections.Generic.List<T> list, int index, T b)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            var path = string.Empty;
            if (ret.Success)
            {
                member = ret.Data.ToString();
                path = $"'$[{member}]'";
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                path = $"concat('$[',{member},']')";
            }
            var doc = arr[0];
            return json_array_insert(doc, path, arr[1]);
        }
        private object hook24(object arg)
        {
            //bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)
            //bool System.Linq.Enumerable.All<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)
            //bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var isAll = method.Name.Contains("All<TSource>");
            var isAnyNoLambda = false;
            if (!isAll) isAnyNoLambda = !method.Name.Contains("predicate");
            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            if (isAnyNoLambda)
            {
                return $"json_length({op1})>0";
            }
            var lambda = call.Arguments[1] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
            if (isAll)
            {
                return $"""
                        json_length({op1})=(select count(1) from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t where {op2})
                        """;
            }
            return $"""
                    0<(select 1 from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t where {op2} limit 1)
                    """;
        }
        private object hook25(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.List<T> list, T item)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, T item)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var op2Doc = string.Empty;
            var op2Val = string.Empty;
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            if (ret.Success)
            {
                op2Doc = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Doc).res;
                op2Val = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Value).res;
            }
            else op2Doc = op2Val = ctx.Visit(call.Arguments[1]);
            return $"case when json_contains({op1},{op2Doc}) then {op1} else json_merge_preserve({op1},json_array({op2Val})) end";
        }
        private object hook26(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)
            //System.Collections.Generic.IList<T DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var isDistinct = false;
            if (method.Name.Contains("isDistinct"))
            {
                var ret3 = ctx.GetConstant(call.Arguments[2], ctx.MidValues, null);
                if (!ret3.Success) throw new Exception($"方法 list.AddRangeIfNotExistFluent(arr,isDistinct) 中的 isDistinct 必须是常量!");
                isDistinct = (bool)ret3.Data;
            }
            var op2 = string.Empty;
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            if (ret.Success)
            {
                op2 = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Doc).res;
            }
            else op2 = ctx.Visit(call.Arguments[1]);
            if (isDistinct)
            {
                return $"""
                        (select json_arrayagg(i) from(  
                            select i from json_table({op1},'$[*]' columns(i json path '$')) t
                            union
                            select i from json_table({op2},'$[*]' columns(i json path '$')) t
                        ) t)
                        """;
            }
            else
            {
                return $"""
                        (select json_arrayagg(i) from(  
                            select i from json_table({op1},'$[*]' columns(i json path '$')) t
                            union all
                            select distinct i from json_table({op2},'$[*]' columns(i json path '$')) t where t.i not in (select i from json_table({op1},'$[*]' columns(i json path '$')) t)
                        ) t)
                        """;
            }
        }
        private object hook27(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, T b)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var op2 = string.Empty;
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            if (ret.Success)
            {
                op2 = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Value).res;
            }
            else op2 = ctx.Visit(call.Arguments[1]);
            return $"(select json_arrayagg(t.i) from json_table({op1},'$[*]' columns(i json path '$')) t where t.i<>{op2})";
        }
        private object hook28(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, System.Predicate<T> match)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var lambda = call.Arguments[1] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);

            var name = ctx.db.AddQuote("a" + lambda.Parameters.First().Name);
            return $"""
                    (select json_arrayagg(t.{name}) from json_table({op1},'$[*]' columns({name} json path '$',{ctx.JsonTableParaToColumns(jsonTablePara)}))t where not ({op2}))
                    """;
        }
        private object hook29(object arg)
        {
            //int System.Collections.Generic.List<T>.IndexOf(T item)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;
            var method = ctx.Reflect;

            var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var op2 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Value])[0];
            return $"(select t.idx-1 from(select row_number() over() as idx,t.i from json_table({op1}, '$[*]' columns (i json path '$')) t) t where t.i={op2} limit 1)";
        }
        private object hook30(object arg)
        {
            //int System.Collections.Generic.List<T>.FindIndex(System.Predicate<T> match)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var lambda = call.Arguments[0] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
            return $"(select idx-1 from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} limit 1)";
        }
        private object hook31(object arg)
        {
            //TResult DotNetCommon.Extensions.ObjectExtensions.ModifyByDto<TResult>(TResult baseObj, System.Object dto)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var op2 = ctx.GetJsonSqlSeg([call.Arguments[1]], [EnumJsonAcceptAsType.Doc])[0];
            return $"json_merge_patch({op1},{op2})";
        }
        private object hook32(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, System.Func<T, T> func)
            //T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, System.Func<T, T> func)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, System.Func<T, T> func)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            var path = string.Empty;
            if (ret.Success)
            {
                member = ret.Data.ToString();
                path = $"'$[{member}]'";
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                path = $"concat('$[',{member},']')";
            }

            var lambda = call.Arguments[2] as LambdaExpression;
            var para = lambda.Parameters.First();
            var paraAlias = memberAccess(doc, path, ret.Success, para.Type);
            ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
            var val = ctx.Visit(lambda);
            ctx.RemoveParameter(para);
            return json_set(doc, path, val);
        }

        private object hook33(object arg)
        {
            //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, System.Func<T, bool> filter, System.Func<T, T> func)
            //T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, System.Func<T, bool> filter, System.Func<T, T> func)
            //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, System.Func<T, bool> filter, System.Func<T, T> func)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var lambda1 = call.Arguments[1] as LambdaExpression;
            var p = lambda1.Parameters.First();
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda1);

            var lambda2 = call.Arguments[2] as LambdaExpression;
            var _para = lambda2.Parameters.First();
            ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(_para, p.Name), true);
            var (jsonTablePara2, op3) = ctx.JsonTableSpread(lambda2);
            ctx.RemoveParameter(_para);
            jsonTablePara.AddRange(jsonTablePara2);
            jsonTablePara.Add((p.Name, null, "json"));
            return $"""
                    (select json_arrayagg(case when {op2} then {op3} else t.`{p.Name}` end) from json_table({doc},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t)
                    """;
        }

        private object hook34(object arg)
        {
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
            return hook32(arg);
        }
        private object hook35(object arg)
        {
            //int DotNetCommon.Extensions.JsonExtensions.FindIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)
            //int DotNetCommon.Extensions.ArrayExtensions.FindIndex<T>(T[] arr, System.Predicate<T> match)
            //int DotNetCommon.Extensions.ListExtensions.FindIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var lambda = call.Arguments[1] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
            return $"(select idx-1 from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} limit 1)";
        }
        private object hook36(object arg)
        {
            //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                member = $"concat('$.',{member})";
            }

            var lambda = call.Arguments[2] as LambdaExpression;
            var para = lambda.Parameters.First();
            var paraAlias = memberAccess(doc, member, ret.Success, para.Type);
            ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
            var val = ctx.Visit(lambda);
            ctx.RemoveParameter(para);
            return json_set(doc, member, val);
        }

        private object hook37(object arg)
        {
            //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Func<System.Text.Json.Nodes.JsonNode, bool> filter, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
            return hook33(arg);
        }
        private object hook38(object arg)
        {
            //int System.Collections.Generic.List<T>.FindLastIndex(System.Predicate<T> match)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
            var lambda = call.Arguments[0] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
            return $"(select idx-1 as idx from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} order by t.idx desc limit 1)";
        }
        private object hook39(object arg)
        {
            //int DotNetCommon.Extensions.JsonExtensions.FindLastIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)
            //int DotNetCommon.Extensions.ArrayExtensions.FindLastIndex<T>(T[] arr, System.Predicate<T> match)
            //int DotNetCommon.Extensions.ListExtensions.FindLastIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var lambda = call.Arguments[1] as LambdaExpression;
            var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
            return $"(select idx-1 as idx from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} order by t.idx desc limit 1)";
        }
        private object hook40(object arg)
        {
            //TSource System.Linq.Enumerable.ElementAt<TSource>(System.Collections.Generic.IEnumerable<TSource> source, int index)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            string member;
            if (ret.Success)
            {
                //常量
                member = $"'$[{ret.Data.To<int>()}]'";
            }
            else
            {
                //变量
                var op2 = ctx.Visit(call.Arguments[1]);
                member = $"concat('$[',{op2},']')";
            }
            return memberAccess(op1, member, ret.Success, call.Type);
        }
        private object hook41(object arg)
        {
            //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)
            //System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)
            var ctx = arg as MethodHookContext;
            var call = ctx.Expression as MethodCallExpression;

            var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
            var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
            var member = string.Empty;
            if (ret.Success)
            {
                member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
            }
            else
            {
                member = ctx.Visit(call.Arguments[1]);
                member = $"concat('$.',{member})";
            }

            var lambda = call.Arguments[2] as LambdaExpression;
            var para = lambda.Parameters.First();
            var paraAlias = memberAccess(doc, member, ret.Success, para.Type);
            ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
            var val = ctx.Visit(lambda);
            ctx.RemoveParameter(para);
            return json_set(doc, member, val);
        }

        #region 废弃 SetMemberFluent
        //private object hook42_1(object arg)
        //{
        //    //T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T, TValue>(T obj, System.Linq.Expressions.Expression<System.Func<T, TValue>> propSelect, System.Func<TValue, TValue> setValue)
        //    var ctx = arg as MethodHookContext;
        //    var call = ctx.Expression as MethodCallExpression;

        //    var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        //    var para1 = call.Arguments[1];
        //    para1 = (para1 as UnaryExpression).Operand;
        //    var lambda1 = para1 as LambdaExpression;
        //    var propNames = ExpressionHelper.GetInitOrReturnPropNames(lambda1);
        //    if (propNames.IsNullOrEmpty()) throw new Exception($"无法从lambda表达式中找到成员: {lambda1}");

        //    var lambda = call.Arguments[2] as LambdaExpression;
        //    var para = lambda.Parameters.First();
        //    var multi = new List<(string jsonPathWrap, string val)>();
        //    foreach (var prop in propNames)
        //    {
        //        var path = ctx.db.GetJsonPathWrapByPropertyName(prop);
        //        var paraAlias = memberAccess(doc, path, true, para.Type);
        //        ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
        //        var val = ctx.Visit(lambda);
        //        ctx.RemoveParameter(para);
        //        multi.Add((path, val));
        //    }
        //    return json_set_multi(doc, multi);
        //}

        //private object hook42_2(object arg)
        //{
        //    //T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T>(T obj, string propName, System.Object val)
        //    var ctx = arg as MethodHookContext;
        //    var call = ctx.Expression as MethodCallExpression;

        //    var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        //    var para1 = call.Arguments[1];
        //    var ret = ctx.GetConstant(para1, ctx.MidValues, null);
        //    var member = string.Empty;
        //    if (ret.Success)
        //    {
        //        member = ctx.db.GetJsonPathWrapByPropertyName(ret.Data.ToString());
        //    }
        //    else
        //    {
        //        member = ctx.Visit(call.Arguments[0]);
        //        member = $"concat('$.',{member})";
        //    }
        //    var val = ctx.Visit(call.Arguments[2]);
        //    return json_set(doc, member, val);
        //}

        //private object hook42_3(object arg)
        //{
        //    //T DotNetCommon.Extensions.GenericExtensions.SetMemberFluent<T, TValue>(T obj, System.Linq.Expressions.Expression<System.Func<T, TValue>> propSelect, TValue val)
        //    var ctx = arg as MethodHookContext;
        //    var call = ctx.Expression as MethodCallExpression;

        //    var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        //    var para1 = call.Arguments[1];
        //    para1 = (para1 as UnaryExpression).Operand;
        //    var lambda1 = para1 as LambdaExpression;
        //    var propNames = ExpressionHelper.GetInitOrReturnPropNames(lambda1);
        //    if (propNames.IsNullOrEmpty()) throw new Exception($"无法从lambda表达式中找到成员: {lambda1}");

        //    var multi = new List<(string jsonPathWrap, string val)>();
        //    var val = ctx.Visit(call.Arguments[2]);
        //    foreach (var prop in propNames)
        //    {
        //        var path = ctx.db.GetJsonPathWrapByPropertyName(prop);
        //        multi.Add((path, val));
        //    }
        //    return json_set_multi(doc, multi);
        //} 
        #endregion

        private string setJsonReturn(string op1, Type returnType)
        {
            var jsonExp = op1;
            if (jsonExp.IsNullOrEmptyOrWhiteSpace()) return jsonExp;
            var returning = GetReturn(returnType);
            if (!jsonExp.StartsWith("json_value("))
            {
                if (returning.IsNullOrEmptyOrWhiteSpace()) return jsonExp;
                if (returning.Contains("datetime(6)")) return $"convert(json_value({jsonExp},'$'),datetime(6))";
                return $"json_value({jsonExp},'$' {returning})";
            }
            if (jsonExp.EndsWith("')"))
            {
                if (returning.IsNullOrEmptyOrWhiteSpace()) return jsonExp;
                if (returning.Contains("datetime(6)"))
                {
                    //使用convert代替,否则查询返回空,反序列化报错
                    //json_value(properties,'$.ext.time') => convert(json_value(properties,'$.ext.time'),datetime(6))
                    return $"convert({jsonExp},datetime(6))";
                }
                //json_value(t.properties,'$.ext.age') => json_value(t.properties,'$.ext.age' returning signed)
                return jsonExp.Substring(0, jsonExp.Length - 1) + " " + returning + ")";
            }
            return jsonExp;
        }

        private string GetReturn(Type type)
        {
            if (type == null) return "";
            var typeCode = type.IsNullable() ? Nullable.GetUnderlyingType(type).GetTypeCode() : type.GetTypeCode();
            var returning = string.Empty;
            switch (typeCode)
            {
                case TypeCode.Boolean:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                    returning = "returning signed";
                    break;
                case TypeCode.SByte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    returning = "returning signed";
                    break;
                case TypeCode.Single:
                    if (this.IsMySqlCompatible("8.0.17"))
                    {
                        returning = "returning float";
                    }
                    else
                    {
                        returning = "returning decimal(30,15)";
                    }
                    break;
                case TypeCode.Double:
                    if (this.IsMySqlCompatible("8.0.17"))
                    {
                        returning = "returning double";
                    }
                    else
                    {
                        returning = "returning decimal(30,15)";
                    }
                    break;
                case TypeCode.Decimal:
                    returning = "returning decimal(30,15)";
                    break;
                case TypeCode.String:
                case TypeCode.Char:
                    returning = "returning char";
                    break;
                case TypeCode.DateTime:
                    returning = "returning datetime(6)";
                    break;
                default:
                    break;
            }
            if (returning == string.Empty)
            {
                var typeClassFullName = type.GetClassFullName();
                switch (typeClassFullName)
                {
                    case "string":
                        returning = "returning char";
                        break;
                    case "System.DateTime":
                    case "System.DateTime?":
                        returning = "returning decimal(30,15)";
                        break;
                    case "System.DateOnly":
                    case "System.DateOnly?":
                        returning = "returning date";
                        break;
                    case "System.TimeSpan":
                    case "System.TimeSpan?":
                    case "System.TimeOnly":
                    case "System.TimeOnly?":
                        returning = "returning time(6)";
                        break;
                    default:
                        break;
                }
            }
            return returning;
        }
        private string getJsonFormatFromSqlSeg(string op1)
        {
            //new{}.ToJsonObject
            //new{}.ToJsonArray
            //str.ToObject<T>

            //select cast('null' as json)
            //select cast(json_value('[1]','$[0]') as json)
            var jsonExp = op1;
            if (jsonExp.StartsWith("json_object") || jsonExp.StartsWith("json_array")) return jsonExp;
            if (jsonExp == null) return "cast('null' as json)";
            return $"cast({jsonExp} as json)";
        }
        private object getJsonArrayAppend(string op1, string op2)
        {
            return $"json_array_append({op1},'$',{op2})";
        }
        private object getJsonArrayMergePreserve(string op1, string op2)
        {
            return $"json_merge_preserve({op1},{op2})";
        }
        private string getJsonArrayRemove(string op1, object op2, bool isConstant)
        {
            if (isConstant)
            {
                //常量
                var idx = (int)op2;
                return $"json_remove({op1},'$[{idx}]')";
            }
            //非常量
            var str = (string)op2;
            //为什么这么写?
            //select json_remove('[1,2,3]',concat('$[',null,']')) //输出null, 这个破坏力太强
            //select json_remove('[1,2,3]','$.null') //仍然输出 [1,2,3]
            return $"json_remove({op1},ifnull(concat('$[',{str},']'),'$.null'))";
        }

        private string getJsonObjectRemove(string op1, string jsonPathWrap)
        {
            return $"json_remove({op1},{jsonPathWrap})";
        }
    }
}